package se.sll.service.catalogue.idmap.service;

import se.sll.service.catalogue.common.rest.response.VMIdMapResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.validation.Validator;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.xml.xpath.XPathExpressionException;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import se.sll.itintegration.catalogue.mek.ejb.HSAMapEjb;
import se.sll.itintegration.catalogue.mek.ejb.MekEjb;
import se.sll.itintegration.catalogue.mek.ejb.SKLEjb;
import se.sll.itintegration.catalogue.mek.ejb.VMEjb;
import se.sll.service.catalogue.common.rest.request.HSAIdMapCreateRequest;
import se.sll.service.catalogue.common.rest.request.HSAIdMapUpdateRequest;
import se.sll.service.catalogue.common.rest.request.SKLIdMapCreateRequest;
import se.sll.service.catalogue.common.rest.request.SKLIdMapUpdateRequest;
import se.sll.service.catalogue.common.rest.request.VMIdMapCreateRequest;
import se.sll.service.catalogue.common.rest.request.VMIdMapUpdateRequest;
import se.sll.service.catalogue.common.rest.response.HSAIdMapResponse;
import se.sll.service.catalogue.common.rest.response.KombikaMapResponse;
import se.sll.service.catalogue.common.rest.response.SKLIdMapResponse;
import se.sll.service.catalogue.common.validation.FacadeRESTValidator;
import se.sll.service.catalogue.idmap.entity.HSAEntity;
import se.sll.service.catalogue.idmap.entity.SKLEntity;
import se.sll.service.catalogue.idmap.entity.VMEntity;
import se.sll.service.catalogue.common.exception.ServiceCatalogueException;
import se.sll.service.catalogue.common.exception.ServiceErrorCode;
import se.sll.service.catalogue.common.exception.ServiceException;

/**
 *
 * @author khaleddaham
 */
@Path("/")
@Slf4j
@Stateless
public class IdMapService {

    @Inject
    private MekEjb mekEjb;
    @Inject
    private HSAMapEjb idMapEjb;
    @Inject
    private SKLEjb sklEjb;
    @Inject
    private VMEjb vmEjb;
    @Inject
    private Validator validator;
    private static final String T_MEDIA_TYPE = "application/json; charset=UTF-8";
    private final FacadeRESTValidator restValidator = new FacadeRESTValidator();

    private static final boolean FEATURE_GET_KOMBIKA_FOR_HSA = false;

    /**
     *
     * @param kombika
     * @return
     * @throws se.sll.service.catalogue.common.exception.ServiceException
     */
    @GET
    @Path("/kombika/{kombika}/hsa")
    @Produces(T_MEDIA_TYPE)
    public KombikaMapResponse getHSAByKombika(@PathParam("kombika") String kombika) throws ServiceException {
        log.debug(kombika);

        String hsaId;
        try {
            hsaId = mekEjb.getHsaID(kombika);
        } catch (XPathExpressionException e) {
            log.error("getHSAByKombika() failed", e);
            throw new ServiceCatalogueException();
        }
        return new KombikaMapResponse(kombika, hsaId, "");
    }

    @GET
    @Path("/hsa/{hsaId}/kombika")
    @Produces(T_MEDIA_TYPE)
    public KombikaMapResponse getKombikaByHSA(@PathParam("hsaId") String hsaId) throws ServiceException {
        log.debug(hsaId);
        String kombika;
        kombika = mekEjb.getKombika(hsaId);
        return new KombikaMapResponse(kombika, hsaId, "");
    }

    @GET
    @Path("/skl/{sklId}/hsa")
    @Produces(T_MEDIA_TYPE)
    public SKLEntity getHSABySKL(@PathParam("sklId") String sklId) throws ServiceException {
        return sklEjb.findByAttributeValue("sklId", sklId).get(0);
    }

    @GET
    @Path("/hsa/{hsaId}/skl")
    @Produces(T_MEDIA_TYPE)
    public SKLEntity getSKLByHSA(@PathParam("hsaId") String hsaId) throws ServiceException {
        return sklEjb.findByAttributeValue("hsaId", hsaId).get(0);
    }

    /**
     * List all Vårdmarknad id's.
     *
     * @return
     * @throws se.sll.service.catalogue.common.exception.ServiceException
     */
    @SuppressWarnings("unchecked")
    @GET
    @Path("/")
    @Produces(T_MEDIA_TYPE)
    public List<VMIdMapResponse> listAll() throws ServiceException {
        List<VMEntity> vmList = vmEjb.findAll();
        List<VMIdMapResponse> response = new ArrayList<>();
        for (VMEntity vm : vmList) {
            if (vm.getHsaId() == null) {
                response.add(
                        new VMIdMapResponse(null, null, null,
                                vm.getVmId(), vm.getVmCareGiverPartID(), vm.getVmCareGiverName(), vm.getVmCareGiverPartName(), "", vm.getHsaId().getSklId(), 
                                vm.getHsaId().isAllowExport(), vm.isAllowImport()));
            } else {
                response.add(
                        new VMIdMapResponse(vm.getHsaId().getHsaId(), vm.getHsaId().getEkId(), vm.getHsaId().getRelativeDistinguishedName(),
                                vm.getVmId(), vm.getVmCareGiverPartID(), vm.getVmCareGiverName(), vm.getVmCareGiverPartName(), "", vm.getHsaId().getSklId(),
                                vm.getHsaId().isAllowExport(), vm.isAllowImport()));
            }
        }
        if (FEATURE_GET_KOMBIKA_FOR_HSA) {
            for (VMIdMapResponse id : response) {
                id.setKombika(mekEjb.getKombika(id.getHsaId()));
            }
        }
        return response;
    }

    /**
     * List methods
     */
    /**
     * List all Vårdmarknad id's.
     *
     * @return
     * @throws se.sll.service.catalogue.common.exception.ServiceException
     */
    @SuppressWarnings("unchecked")
    @GET
    @Path("/vm")
    @Produces(T_MEDIA_TYPE)
    public List<VMIdMapResponse> listVm() throws ServiceException {
        List<VMEntity> vmList = vmEjb.findAll();
        List<VMIdMapResponse> response = new ArrayList<>();
        for (VMEntity vm : vmList) {
            if (vm.getHsaId() == null) {
                response.add(
                        new VMIdMapResponse(null, null, null,
                                vm.getVmId(), vm.getVmCareGiverPartID(), vm.getVmCareGiverName(), vm.getVmCareGiverPartName(), "", "", 
                                vm.getHsaId().isAllowExport(), vm.isAllowImport()));
            } else {
                response.add(
                        new VMIdMapResponse(vm.getHsaId().getHsaId(), vm.getHsaId().getEkId(), vm.getHsaId().getRelativeDistinguishedName(),
                                vm.getVmId(), vm.getVmCareGiverPartID(), vm.getVmCareGiverName(), vm.getVmCareGiverPartName(), "", "", 
                                vm.getHsaId().isAllowExport(), vm.isAllowImport()));
            }
        }
        return response;
    }

    /**
     * List all SKL id's.
     *
     * @return
     * @throws se.sll.service.catalogue.common.exception.ServiceException
     */
    @SuppressWarnings("unchecked")
    @GET
    @Path("/skl")
    @Produces(T_MEDIA_TYPE)
    public List<SKLIdMapResponse> listSkl() throws ServiceException {
        List<SKLEntity> sklList = sklEjb.findAll();
        List<SKLIdMapResponse> response = new ArrayList<>();
        for (SKLEntity skl : sklList) {
            List<String> hsaList = new ArrayList<>();
            for (HSAEntity hsa : skl.getHsaId()) {
                hsaList.add(
                        hsa.getHsaId());
            }
            response.add(
                    new SKLIdMapResponse(hsaList, skl.getSklId(), skl.isExport()));
        }
        return response;
    }

    /**
     * List all HSA id's.
     *
     * @return
     * @throws se.sll.service.catalogue.common.exception.ServiceException
     */
    @SuppressWarnings("unchecked")
    @GET
    @Path("/hsa")
    @Produces(T_MEDIA_TYPE)
    public List<HSAIdMapResponse> listHsa() throws ServiceException {
        List<HSAEntity> hsaList = (List<HSAEntity>) idMapEjb.findAll();
        List<HSAIdMapResponse> response = new ArrayList<>();
        for (HSAEntity hsa : hsaList) {
            response.add(new HSAIdMapResponse(hsa.getHsaId(), hsa.getEkId(), hsa.getRelativeDistinguishedName()));
        }
        return response;
    }

    /**
     * List all Kombika id's
     *
     * @return
     * @throws se.sll.service.catalogue.common.exception.ServiceException
     */
    @SuppressWarnings("unchecked")
    @GET
    @Path("/kombika")
    @Produces(T_MEDIA_TYPE)
    public List<HSAIdMapResponse> listKombika() throws ServiceException {
        List<HSAIdMapResponse> response = new ArrayList<>();
        NodeList nl;
        try {
            nl = mekEjb.listKombika();
            for (int i = 0; i < nl.getLength(); i++) {
                Node childNode = nl.item(i);
            }
        } catch (XPathExpressionException ex) {
            Logger.getLogger(IdMapService.class.getName()).log(Level.SEVERE, null, ex);
        }
        return response;
    }

    /**
     * Create methods
     */
    /**
     *
     * @param request
     * @throws ServiceException
     */
    @SuppressWarnings("unchecked")
    @POST
    @Path("/hsa")
    @Consumes(T_MEDIA_TYPE)
    @Produces(T_MEDIA_TYPE)
    public void createHSA(HSAIdMapCreateRequest request) throws ServiceException {
        restValidator.validate(request, validator);
        List<HSAEntity> existing = idMapEjb.findByAttributeValue("hsaId", request.getHSAId());
        if (existing.isEmpty()) {
            HSAEntity hsa = new HSAEntity();
            hsa.setHsaId(request.getHSAId());
            hsa.setRelativeDistinguishedName(request.getRelativeDistinguishedName());
            idMapEjb.create(hsa);
        } else {
            throw new ServiceCatalogueException(ServiceErrorCode.HSAID_ALREADY_EXISTS_HSAID, request.getHSAId());
        }
    }

    /**
     *
     * @param request
     * @throws ServiceException
     */
    @SuppressWarnings("unchecked")
    @POST
    @Path("/skl")
    @Consumes(T_MEDIA_TYPE)
    @Produces(T_MEDIA_TYPE)
    public void createSKL(SKLIdMapCreateRequest request) throws ServiceException {
        restValidator.validate(request, validator);
        List<HSAEntity> hsaList = new ArrayList<>();
        for (String hsa : request.getHsa()) {
            List<HSAEntity> existingHsa = idMapEjb.findByAttributeValue("hsaId", hsa);
            if (existingHsa.isEmpty()) {
                idMapEjb.create(new HSAEntity(hsa, "", "", ""));
                existingHsa = idMapEjb.findByAttributeValue("hsaId", hsa);
            }
            hsaList.add(existingHsa.get(0));
        }
        List<SKLEntity> existing = sklEjb.findByAttributeValue("sklId", request.getSklId());

        if (existing.isEmpty()) {
            SKLEntity skl = new SKLEntity();
            skl.setHsaId(hsaList);
            skl.setExport(request.isAllowExport());
            skl.setSklId(request.getSklId());
            sklEjb.create(skl);
        } else {
            throw new ServiceCatalogueException(ServiceErrorCode.SKLID_ALREADY_EXISTS_SKLID, request.getSklId());
        }
    }

    /**
     *
     * @param request
     * @throws ServiceException
     */
    @SuppressWarnings("unchecked")
    @POST
    @Path("/vm")
    @Consumes(T_MEDIA_TYPE)
    @Produces(T_MEDIA_TYPE)
    public void createVM(VMIdMapCreateRequest request) throws ServiceException {
        log.info(request.toString());
        restValidator.validate(request, validator);
        List<HSAEntity> existingHsa = null;
        if (request.getHsaId() != null) {
            existingHsa = idMapEjb.findByAttributeValue("hsaId", request.getHsaId());
            if (existingHsa.isEmpty()) {
                idMapEjb.create(new HSAEntity(request.getHsaId(), request.getEkId(), request.getRelativeDistinguishedName(), ""));
                existingHsa = idMapEjb.findByAttributeValue("hsaId", request.getHsaId());
            }
        }

        List<VMEntity> existing = vmEjb.findVmCareGiver(request.getVmId(), request.getVmCareGiverPartId());
        if (existing.isEmpty()) {
            VMEntity vm = new VMEntity();
            vm.setAllowImport(request.isAllowImport());
            if (existingHsa != null) {
                vm.setHsaId(existingHsa.get(0));
            }
            vm.setVmCareGiverName(request.getVmCareGiverName());
            vm.setVmCareGiverPartName(request.getVmCareGiverPartName());
            vm.setVmCareGiverPartID(request.getVmCareGiverPartId());
            vm.setVmId(request.getVmId());
            vmEjb.create(vm);
        } else {
            throw new ServiceCatalogueException(ServiceErrorCode.VMCAREGIVERPARTID_ALREADY_EXISTS_VMID_VMCAREGIVERPARTID, request.getVmId(), request.getVmCareGiverPartId());
        }
    }

    /**
     *
     * @param request
     * @throws ServiceException
     */
    @SuppressWarnings("unchecked")
    @PUT
    @Path("/skl")
    @Consumes(T_MEDIA_TYPE)
    @Produces(T_MEDIA_TYPE)
    public void updateSkl(SKLIdMapUpdateRequest request) throws ServiceException {
        restValidator.validate(request, validator);
        List<HSAEntity> hsaList = new ArrayList<>();
        for (String hsa : request.getHsa()) {
            List<HSAEntity> existingHsa = idMapEjb.findByAttributeValue("hsaId", hsa);
            if (existingHsa.isEmpty()) {
                idMapEjb.create(new HSAEntity(hsa, "", "", ""));
                existingHsa = idMapEjb.findByAttributeValue("hsaId", hsa);
            }
            hsaList.add(existingHsa.get(0));
        }
        List<SKLEntity> existing = sklEjb.findByAttributeValue("sklId", request.getSklId());
        if (!existing.isEmpty()) {
            SKLEntity skl = existing.get(0);
            skl.setExport(request.isAllowExport());
            skl.setHsaId(hsaList);
            sklEjb.edit(skl);
        } else {
            throw new ServiceCatalogueException(ServiceErrorCode.SKLID_NOT_FOUND_SKLID, request.getSklId());
        }
    }

    @SuppressWarnings("unchecked")
    @PUT
    @Path("/")
    @Consumes(T_MEDIA_TYPE)
    @Produces(T_MEDIA_TYPE)
    public VMIdMapUpdateRequest updateIdMap(VMIdMapUpdateRequest request) throws ServiceException {
        log.debug(request.toString());
        restValidator.validate(request, validator);
        List<HSAEntity> existingHsa = idMapEjb.findByAttributeValue("hsaId", request.getHsaId());
        if (existingHsa.isEmpty()) {
            idMapEjb.create(new HSAEntity(request.getHsaId(), request.getEkId(), "", request.getSklId()));
            existingHsa = idMapEjb.findByAttributeValue("hsaId", request.getHsaId());
        } else {
            existingHsa.get(0).setEkId(request.getEkId());
        }
        List<VMEntity> existing = vmEjb.findByAttributeValue("vmId", request.getVmId());
        if (!existing.isEmpty()) {
            existingHsa.get(0).setAllowExport(request.isAllowExport());
            existingHsa.get(0).setSklId(request.getSklId());
            VMEntity vm = existing.get(0);
            vm.setAllowImport(request.isAllowImport());            
            vm.setHsaId(existingHsa.get(0));
            vm.setVmCareGiverName(request.getVmCareGiverName());
            vm.setVmCareGiverPartName(request.getVmCareGiverPartName());
            vmEjb.edit(vm);
            idMapEjb.edit(existingHsa.get(0));
        } else {
            throw new ServiceCatalogueException(ServiceErrorCode.VMID_NOT_FOUND_VMID, request.getVmId());
        }
        return request;
    }

    @SuppressWarnings("unchecked")
    @PUT
    @Path("/hsa")
    @Consumes(T_MEDIA_TYPE)
    @Produces(T_MEDIA_TYPE)
    public void updateHsa(HSAIdMapUpdateRequest request) throws ServiceException {
        log.debug(request.toString());
        restValidator.validate(request, validator);
        List<HSAEntity> existing = idMapEjb.findByAttributeValue("hsaId", request.getHsaId());
        if (!existing.isEmpty()) {
            HSAEntity hsa = existing.get(0);
            hsa.setEkId(request.getRegionalHsaId());
            hsa.setRelativeDistinguishedName(request.getRelativeDistinguishedName());
            idMapEjb.edit(hsa);
        } else {
            throw new ServiceCatalogueException(ServiceErrorCode.HSAID_NOT_FOUND_HSAID, request.getHsaId());
        }
    }

    /**
     * Delete methods
     */
    /**
     *
     * @param sklId
     * @throws se.sll.service.catalogue.common.exception.ServiceException
     */
    @SuppressWarnings("unchecked")
    @DELETE
    @Path("/skl/{sklId}")
    @Consumes(T_MEDIA_TYPE)
    @Produces(T_MEDIA_TYPE)
    public void deleteSkl(@PathParam("sklId") String sklId) throws ServiceException {
        List<SKLEntity> existing = sklEjb.findByAttributeValue("sklId", sklId);
        if (!existing.isEmpty()) {
            sklEjb.remove(existing.get(0));
        } else {
            throw new ServiceCatalogueException(ServiceErrorCode.SKLID_NOT_FOUND_SKLID, sklId);
        }
    }

    @SuppressWarnings("unchecked")
    @DELETE
    @Path("/vm/{vmId}")
    @Consumes(T_MEDIA_TYPE)
    @Produces(T_MEDIA_TYPE)
    public void deleteVm(@PathParam("vmId") String vmId) throws ServiceException {
        List<VMEntity> existing = vmEjb.findByAttributeValue("vmId", vmId);
        if (!existing.isEmpty()) {
            vmEjb.remove(existing.get(0));
        } else {
            throw new ServiceCatalogueException(ServiceErrorCode.VMID_NOT_FOUND_VMID, vmId);
        }
    }

    @SuppressWarnings("unchecked")
    @DELETE
    @Path("/hsa/{hsaId}")
    @Consumes(T_MEDIA_TYPE)
    @Produces(T_MEDIA_TYPE)
    public void deleteHsa(@PathParam("hsaId") String hsaId) throws ServiceException {
        List<HSAEntity> existing = idMapEjb.findByAttributeValue("hsaId", hsaId);
        if (!existing.isEmpty()) {
            idMapEjb.remove(existing.get(0));
        } else {
            throw new ServiceCatalogueException(ServiceErrorCode.HSAID_NOT_FOUND_HSAID, hsaId);
        }
    }
}
